package cloudspace.vm.io.console;

import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.zkoss.lang.Threads;

/**
 * An interface for info Streams. These info streams provide lines of console
 * output to the view. The number of lines that can be stored in an info stream
 * is limited by Long.MAX_SIZE.
 */
public class InfoStream extends OutputStream
{
	private long lastPrint;
	private static final int MAXCACHESIZE = 1000;

	private String streamID;
	/** The timestamp pat. */
	// private static final Pattern timestampPat = Pattern
	// .compile("\\{\\s*([^\\}]+)\\s*\\}.*");;

	/** The date format. */
	private static final DateFormat dateFormat = new SimpleDateFormat(
			"HH:mm dd/MM/yy");
	String partialLine;
	private MemoryManagedLineCache lineCache;
	private int currentLine;

	/**
	 * This gets the lowest line number stored in this line cache.
	 * 
	 * @return the lowest index accessible
	 */
	public long getLineCacheStart()
	{
		return lineCache.getStartIndex();
	}

	/**
	 * Instantiates a new abstract info stream. This stream is automatically
	 * registered to the console.
	 * 
	 * @param identifier
	 *            the identifier
	 */
	public InfoStream(String identifier)
	{
		super();
		streamID = identifier;
		init();
	}

	private void init()
	{
		currentLine = 0;
		partialLine = "";
		lineCache = new MemoryManagedLineCache(MAXCACHESIZE);
		lastPrint = 0;

	}

	@Override
	public void write(int b)
	{
		
		lastPrint = Calendar.getInstance().getTime().getTime();
		if ((char) b == '\n')
		{
			completeLine(partialLine);
			partialLine = "";
		}
		else
		{
			partialLine += (char) b;
		}
	}

	private synchronized void completeLine(String line)
	{
		if(lastPrint > Calendar.getInstance().getTime().getTime()-100)
		{
//			try
//			{
//				Thread.sleep(100);
//			}
//			catch (InterruptedException e)
//			{
//				e.printStackTrace();
//			}
			
		}
		// this is a backup to flush the stream if the counter goes too high
		if (currentLine == Long.MAX_VALUE)
			init();
		currentLine++;
		Calendar cal = Calendar.getInstance();
		String curTime = dateFormat.format(cal.getTime());
		InfoStreamLine isl = convert( "{" + curTime + "}", line, cal.getTime().getTime());
		if (isl != null)
		{
			lineCache.add(isl);
		}

	}

	/**
	 * This converts a raw line to a properly formatted line for output. This
	 * appends timestamp and line number.
	 * 
	 * @param readLine
	 *            the line to be properly formatted
	 * 
	 * @return a fully initialized info stream.
	 */
	private InfoStreamLine convert(String tStamp, String readLine, long timeStamp)
	{
		if (!readLine.equals(""))
		{

			try
			{
				return new InfoStreamLine(streamID, tStamp, readLine, timeStamp,
						currentLine);

			}
			catch (IllegalStateException e)
			{
				System.err
						.println("Internal Matcher does not fit: " + readLine);
			}
		}
		return null;
	}

	/**
	 * Get the current line number for the next line in the info stream
	 * 
	 * @return the current line number
	 */
	public long getLineNumber()
	{
		return currentLine;
	}

	/**
	 * Get all the lines stored in the InfoStream starting at a specific index
	 * 
	 * @param indexLine
	 *            index to start the get command from
	 * @return a list of info stream lines starting at the line number passed
	 * @throws FlushedException
	 *             If the info stream line has been flushed, this exception is
	 *             thrown to notify any methods accessing the getLines method
	 *             that it must reset it's index
	 */
	public List<InfoStreamLine> getLines(long indexLine)
			throws FlushedException
	{
		if (indexLine > lineCache.size())
		{
			throw new FlushedException();
		}
		List<InfoStreamLine> subLines = new ArrayList<InfoStreamLine>();
		subLines.addAll(lineCache.subList(indexLine, lineCache.size()));
		return subLines;
	}

	/**
	 * Accesses the stream id for the info stream.
	 * 
	 * @return the stream id.
	 */
	public String getStreamID()
	{
		return streamID;
	}
}